﻿using System;
using System.Globalization;
using System.Collections.Generic;
using ProjectEuler.Linq;
using System.Linq;

namespace ProjectEuler
{
    public struct Rational : IEquatable<Rational>
    {
        private readonly Int32 _n;
        private readonly Int32 _d;

        public Rational(Int32 numerator, Int32 denominator)
        {
            if (denominator == 0)
            {
                throw new ArgumentOutOfRangeException("denominator");
            }
            
            _n = numerator;
            _d = denominator;
        }

        public Int32 Numerator
        {
            get { return _n; }
        }

        public Int32 Denominator
        {
            get { return _d; }
        }

        public override Int32 GetHashCode()
        {
            return _n ^ _d;
        }

        public override Boolean Equals(Object obj)
        {
            if (obj is Rational)
            {
                return Equals((Rational)(obj));
            }

            return false;
        }

        public Boolean Equals(Rational other)
        {
            return
                (Numerator == other.Numerator) &&
                (Denominator == other.Denominator);
        }

        public Rational ToLowestTerms()
        {
            var gcd = GreatestCommonDivisor(_n, _d);

            if (gcd > 1)
            {
                return new Rational(_n / gcd, _d / gcd);
            }

            return this;
        }

        public override String ToString()
        {
            return String.Format(CultureInfo.InvariantCulture, "{0}/{1}", _n, _d);
        }

        public static Boolean operator ==(Rational a, Rational b)
        {
            return a.Equals(b);
        }

        public static Boolean operator !=(Rational a, Rational b)
        {
            return !a.Equals(b);
        }

        public static Int32 GreatestCommonDivisor(Int32 a, Int32 b)
        {
            if ((a == 1) || (b == 1))
            {
                return 1;
            }

            if (a == 0)
            {
                return b;
            }

            while (b != 0)
            {
                if (a > b)
                {
                    a -= b;
                }
                else
                {
                    b -= a;
                }
            }

            return a;
        }
    }
}
